﻿using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace AnimationXMachine
{
    public class Animation
    {
        #region Declarations
        private int frameIndex;
        private int frameDelay;
        private int frameTimer;
        private int lines;
        private Point startPoint;
        private List<Rectangle> animationFrames;
        private string texturePath;

        public Texture2D Texture { private set; get; }
        public bool IsLooping { private set; get; }
        public bool Finished { private set; get; }
        public Vector2 Origin { private set; get; }
        #endregion //Declarations

        #region Constructor
        /// <summary>
        /// An animation cannot be on more than one sprite sheet, but it does
        /// not have to cover an entire sheet.
        /// </summary>
        /// <param name="frameDelay">For how long each frame should last.</param>
        /// <param name="numberOfFrames">How many frames there are on each line.</param>
        /// <param name="height">The height of each frame.</param>
        /// <param name="width">The width of each frame.</param>
        /// <param name="lines">The number of lines in the animation. Each line must have the same width.</param>
        /// <param name="startPoint">The top-left starting point on the sheet.</param>
        /// <param name="texturePath">The path to the texture.</param>
        /// <param name="texture">The actual texture.</param>
        public Animation(int frameDelay, int numberOfFrames,
            int height, int width, int lines, Point startPoint,
            string texturePath, Texture2D texture, bool isLooping)
        {
            this.frameIndex = 0;
            this.frameTimer = 0;
            this.frameDelay = frameDelay;
            this.animationFrames = new List<Rectangle>(numberOfFrames * lines);
            this.lines = lines;
            this.startPoint = startPoint;
            this.texturePath = texturePath;
            this.Texture = texture;
            this.IsLooping = isLooping;
            for (int j = 0; j < lines; j++)
            {
                for (int i = 0; i < numberOfFrames; i++)
                {
                    animationFrames.Add(new Rectangle(startPoint.X, startPoint.Y, width, height));
                    startPoint.X += width;
                }
                startPoint.Y += height;
                startPoint.X -= width * numberOfFrames;
            }
            this.Origin = new Vector2(animationFrames[0].Center.X, animationFrames[0].Center.Y);
        }
        #endregion //Constructor

        #region Update
        /// <summary>
        /// Updates the animation; use GetFrame when drawing to get the most recent frame.
        /// </summary>
        /// <param name="elapsedTime"></param>
        /// <returns>True if the animation has just finished one complete run.</returns>
        public bool Update(int elapsedTime)
        {
            if ((frameTimer += elapsedTime) >= frameDelay)
            {
                frameTimer = 0;
                if (frameIndex == animationFrames.Count - 1)
                {
                    if (IsLooping)
                        frameIndex = 0;
                    return true;
                }
                else
                {
                    frameIndex++;
                    return false;
                }
            }
            else return false;
        }
        #endregion //Update

        #region Methods & Properties
        /// <summary>
        /// Resets the animation.
        /// </summary>
        public void ResetFrameTime()
        {
            frameIndex = 0;
            frameTimer = 0;
        }

        /// <summary>
        /// Sets the animations frame to be the same as another animation's.
        /// </summary>
        /// <param name="anim"></param>
        public void SetFrameTime(Animation anim)
        {
            frameIndex = anim.frameIndex;
            frameTimer = anim.frameTimer;
        }

        /// <summary>
        /// Gets the current frame of the sprite sheet.
        /// </summary>
        public Rectangle GetFrame
        {
            get { return animationFrames[frameIndex]; }
        }

        public bool LastFrame
        {
            get { return frameIndex == animationFrames.Count - 1; }
        }

        /// <summary>
        /// Returns the length in time of animationFrames, in ms.
        /// </summary>
        public int AnimationLength
        {
            get { return animationFrames.Count * frameDelay; }
        }

        /// <summary>
        /// Gets the comparison (like height or width) divided by (the number
        /// of frames times the frame delay).
        /// This is how far an animation should traverse during one ms, if it
        /// wants to move during an animation.
        /// It may have some error and/or inefficiency.
        /// </summary>
        public float AnimationDistance(float comparison)
        {
            return comparison / (animationFrames.Count * frameDelay);
        }
        #endregion //Methods & Properties

        public object Clone()
        {
            return new Animation(frameDelay, animationFrames.Count / lines, animationFrames[0].Height,
                animationFrames[0].Width, lines, startPoint, texturePath, Texture, IsLooping);
        }
    }
}